package com.teragadgets.android.gameboy;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.view.KeyEvent;

public class RomController {

	static int[] key = new int[] { KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_A,
			KeyEvent.KEYCODE_W, KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_M,
			KeyEvent.KEYCODE_N, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_ENTER };

	static String romFile = null;
	static int romId = 0;
	static String stateChanged = "";

	static RomRenderer render;
	static Dmgcpu dmgcpu;
	public static boolean draw = true;

	static boolean isPaused;

	/** Compressed file types */
	final static byte bNotCompressed = 0;
	final static byte bZip = 1;
	final byte bJar = 2;
	final static byte bGZip = 3;

	public static boolean loadRom(String state) {
		if (romFile == null)
			return false;
		byte[] data = restoreState(state);
		if (data == null)
			dmgcpu = new Dmgcpu(stripExtention(romFile), openRom(romFile));
		else
			dmgcpu = new Dmgcpu(stripExtention(romFile), openRom(romFile), data);
		loadCartRam();
		return true;
	}

	private static InputStream openRom(String romFileName) {
		byte bFormat;
		boolean bFoundGBROM = false;
		String romName = "None";

		if (romFileName.toUpperCase().indexOf("ZIP") > -1) {
			bFormat = bZip;
		} else if (romFileName.toUpperCase().indexOf("JAR") > -1) {
			bFormat = bZip;
		} else if (romFileName.toUpperCase().indexOf("GZ") > -1) {
			bFormat = bGZip;
		} else {
			bFormat = bNotCompressed;
		}

		// Simplest case, open plain gb or gbc file.
		if (bFormat == bNotCompressed) {
			try {
				romName = stripExtention(romFileName);
				return new FileInputStream(new File(romFileName));
			} catch (Exception e) {
				System.out.println("Cant open file");
				return null;
			}
		}

		// Should the ROM be loaded from a ZIP compressed file?
		if (bFormat == bZip) {
			System.out.println("Loading ZIP Compressed ROM");

			java.util.zip.ZipInputStream zip;

			try {

				zip = new java.util.zip.ZipInputStream(
						new java.io.FileInputStream(romFileName));

				// Check for valid files (GB or GBC ending in filename)
				java.util.zip.ZipEntry ze;

				while ((ze = zip.getNextEntry()) != null) {
					String str = ze.getName();
					if (str.toUpperCase().indexOf(".GB") > -1
							|| str.toUpperCase().indexOf(".GBC") > -1) {
						bFoundGBROM = true;
						romName = stripExtention(str);
						// Leave loop if a ROM was found.
						break;
					}
				}
				// Show an error if no ROM file was found in the ZIP
				if (!bFoundGBROM) {
					System.err.println("No GBx ROM found!");
					throw new java.io.IOException("ERROR");
				}
				System.out.println("Found " + romName);
				return zip;
			} catch (Exception e) {
				System.out.println(e);
				return null;
			}
		}

		if (bFormat == bGZip) {
			System.out.println("Loading GZIP Compressed ROM");
			romName = stripExtention(romFileName);
			try {
				return new java.util.zip.GZIPInputStream(
						new java.io.FileInputStream(romFileName));
			} catch (Exception e) {
				System.out.println("Can't open file");
				return null;
			}
		}
		return null;
	}

	public static String stripExtention(String filename) {
		if (filename == null)
			return null;
		int dotPosition = filename.lastIndexOf('.');

		if (dotPosition != -1) {
			return filename.substring(0, dotPosition);
		} else {
			return filename;
		}
	}

	private static byte[] restoreState(String state) {
		try {
			if (state == null || state.equals("")) {
				System.out.println("No saved state");
				return null;
			}

			int dotPosition = romFile.lastIndexOf('.');
			String saveRamFileName;
			if (dotPosition != -1) {
				saveRamFileName = romFile.substring(0, dotPosition) + "_"
						+ state + ".stt";
			} else {
				saveRamFileName = romFile + state + ".stt";
			}

			BufferedInputStream rs = new BufferedInputStream(
					new FileInputStream(saveRamFileName));

			byte[] data = new byte[rs.available()];
			int result = rs.read(data);
			if (result > 0) {
				System.out.println("State restored: " + saveRamFileName + " "
						+ saveRamFileName);
				return data;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static void runRom(RomRenderer render) {

		RomController.render = render;
		System.out.println("Rom Started");
		dmgcpu.run();
	}

	public static void saveState(String state) {
		try {
			if (dmgcpu == null || state == null)
				return;
			String saveRamFileName = stripExtention(romFile) + "_" + state
					+ ".stt";

			BufferedOutputStream rs = new BufferedOutputStream(
					new FileOutputStream(saveRamFileName));

			rs.write(dmgcpu.flatten());
			rs.close();

			// SharedPreferences pref = getSharedPreferences(
			// "com.teragadgets.android.gameboy", 0);
			// Editor editor = pref.edit();
			// editor.putString("savestate", file);
			// editor.commit();

			Bitmap original = Bitmap.createBitmap(
					GraphicsChip.frameBufferImage, 0, 0, 160, 144);
			Bitmap stateImage = Bitmap.createScaledBitmap(original, 160 / 2,
					144 / 2, true);
			int data[] = new int[(160 / 2) * (144 / 2)];
			stateImage.getPixels(data, 0, 160 / 2, 0, 0, 160 / 2, 144 / 2);
			int id = KiddGBC.database.updateStateImage(romId, state, data);
			KiddGBC.thumbs.remove(id);
			System.out.println("State saved: " + romFile + " "
					+ saveRamFileName);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static boolean keyDown(boolean touch, int keyCode) {
		if (touch) {
			switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_DOWN:
				keyCode = key[3];
				dmgcpu.buttonUp(0);
				dmgcpu.buttonUp(1);
				dmgcpu.buttonUp(2);
				break;
			case KeyEvent.KEYCODE_DPAD_UP:
				keyCode = key[2];
				dmgcpu.buttonUp(0);
				dmgcpu.buttonUp(1);
				dmgcpu.buttonUp(3);
				break;
			case KeyEvent.KEYCODE_DPAD_LEFT:
				keyCode = key[1];
				dmgcpu.buttonUp(0);
				dmgcpu.buttonUp(2);
				dmgcpu.buttonUp(3);
				break;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				keyCode = key[0];
				dmgcpu.buttonUp(1);
				dmgcpu.buttonUp(2);
				dmgcpu.buttonUp(3);
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
				dmgcpu.buttonUp(0);
				dmgcpu.buttonUp(1);
				dmgcpu.buttonUp(2);
				dmgcpu.buttonUp(3);
				break;
			}
		}
		if (dmgcpu != null) {
			for (int i = 0; i < 8; i++) {
				if (keyCode == key[i]) {
					dmgcpu.buttonDown(i);
				}
			}
		}
		return false;
	}

	public static boolean keyUp(int keyCode) {
		if (dmgcpu != null) {
			for (int i = 0; i < 8; i++) {
				if (keyCode == key[i]) {
					dmgcpu.buttonUp(i);
				}
			}
		}
		return false;
	}

	public static final void saveCartRam() {
		try {
			if (romFile == null)
				return;
			int dotPosition = romFile.lastIndexOf('.');
			String saveRamFileName;
			if (dotPosition != -1) {
				saveRamFileName = romFile.substring(0, dotPosition) + ".sav";
			} else {
				saveRamFileName = romFile + ".sav";
			}
			BufferedOutputStream rs = new BufferedOutputStream(
					new FileOutputStream(saveRamFileName));

			byte[][] ram = dmgcpu.cartRam;

			int bankCount = ram.length;
			int bankSize = ram[0].length;
			int size = bankCount * bankSize + 13;

			byte[] b = new byte[size];

			for (int i = 0; i < bankCount; i++)
				System.arraycopy(ram[i], 0, b, i * bankSize, bankSize);

			System.arraycopy(dmgcpu.rtcReg, 0, b, bankCount * bankSize, 5);
			long now = System.currentTimeMillis();
			Dmgcpu.setInt(b, bankCount * bankSize + 5, (int) (now >> 32));
			Dmgcpu.setInt(b, bankCount * bankSize + 9, (int) now);

			for (byte byt : b) {
				rs.write(byt);
			}
			// rs.write(b, 0, b.length);
			rs.close();
			System.out.println("Ram Saved");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static final void loadCartRam() {
		if (romFile == null)
			return;
		try {
			int dotPosition = romFile.lastIndexOf('.');
			String saveRamFileName;
			if (dotPosition != -1) {
				saveRamFileName = romFile.substring(0, dotPosition) + ".sav";
			} else {
				saveRamFileName = romFile + ".sav";
			}
			BufferedInputStream rs = new BufferedInputStream(
					new FileInputStream(saveRamFileName));
			// RecordStore rs = RecordStore.openRecordStore("20R_" + cartID,
			// true);

			if (rs.available() > 0) {
				byte[][] ram = dmgcpu.cartRam;
				int bankCount = ram.length;
				int bankSize = ram[0].length;

				int total = 0;
				int pos = 0;
				int ramPos = 0;
				while (rs.available() > 0) {
					byte b = (byte) rs.read();
					if (pos >= ram[0].length) {
						pos = 0;
						ramPos++;
					}
					if (ramPos >= ram.length)
						break;
					// System.out.println(ramPos + " " + pos);
					ram[ramPos][pos] = b;
					total++;
					pos++;

				}
				// byte[] b = new byte[rs.available()];
				// rs.read(b);// .read();//.getRecord(1);

				// for (int i = 0; i < bankCount; i++)
				// System.arraycopy(b, i * bankSize, ram[i], 0, bankSize);

				if (total == bankCount * bankSize + 13) {
					total = 0;
					pos = 0;
					while (rs.available() > 0 || total < 5) {
						byte b = (byte) rs.read();
						dmgcpu.rtcReg[pos] = b;
						pos++;
						total++;
					}
					// load real time clock
					// System.arraycopy(b, bankCount * bankSize, dmgcpu
					// .getRtcReg(), 0, 5);
					long time = Dmgcpu.getInt(dmgcpu.rtcReg, 5);
					time = (time << 32)
							+ ((long) Dmgcpu.getInt(dmgcpu.rtcReg, 9) & 0xffffffffL);
					time = System.currentTimeMillis() - time;
					dmgcpu.rtcSkip((int) (time / 1000));
				}
			}
			rs.close();// .closeRecordStore();
			System.out.println("Ram Loaded");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
